package pkg

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	v1 "k8s.io/api/admission/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/klog/v2"
)

var (
	scheme = runtime.NewScheme()
	codecs = serializer.NewCodecFactory(scheme)
)

type PatchOperation struct {
	Op    string      `json:"op"`
	Path  string      `json:"path"`
	Value interface{} `json:"value,omitempty"`
}

type webhookHandler struct {
}

func NewWebhookHandler() http.Handler {
	return &webhookHandler{}
}

//The Handle needs to implement ServeHTTP
func (h *webhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		err := fmt.Errorf("invalid method %s, only POST requests are allowed", r.Method)
		w.Write([]byte(err.Error()))
		return
	}
	if contentType := r.Header.Get("Content-Type"); contentType != `application/json` {
		w.WriteHeader(http.StatusBadRequest)
		err := fmt.Errorf("unsupported content type %s, only %s is supported", contentType, `application/json`)
		w.Write([]byte(err.Error()))
		return
	}
	w.Header().Set("Content-Type", "application/json")
	h.changePodScheduler(w, r)
}

func (hook *webhookHandler) changePodScheduler(w http.ResponseWriter, r *http.Request) {
	var admissionReviewReq v1.AdmissionReview
	if err := json.NewDecoder(r.Body).Decode(&admissionReviewReq); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		err = fmt.Errorf("r.Body parsing failed: %v", err)
		w.Write([]byte(err.Error()))
		return
	} else if admissionReviewReq.Request == nil {
		w.WriteHeader(http.StatusBadRequest)
		err = errors.New("request is nil")
		w.Write([]byte(err.Error()))
		return
	}
	klog.V(4).Info("The structure information received by http is :", admissionReviewReq)

	pod := corev1.Pod{}
	obj, _, err := codecs.UniversalDecoder().Decode(admissionReviewReq.Request.Object.Raw, nil, &pod)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		err = fmt.Errorf("decode request object error: %v", err)
		w.Write([]byte(err.Error()))
		return
	}

	if _, ok := obj.(*corev1.Pod); !ok {
		w.WriteHeader(http.StatusInternalServerError)
		err = errors.New("change obj to pod error")
		w.Write([]byte(err.Error()))
		return
	}

	admissionReviewResponse := v1.AdmissionReview{
		TypeMeta: metav1.TypeMeta{
			Kind:       "AdmissionReview",
			APIVersion: "admission.k8s.io/v1",
		},
		Response: &v1.AdmissionResponse{
			UID: admissionReviewReq.Request.UID,
		},
	}
	var patches []PatchOperation
	patchOps := append(patches, PatchOperation{
		Op:    "replace",
		Path:  "/spec/schedulerName",
		Value: "new_scheduler",
	})
	patchBytes, err := json.Marshal(patchOps)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		err = fmt.Errorf("marshal patch operation error: %v", err)
		w.Write([]byte(err.Error()))
		return
	}
	admissionReviewResponse.Response.Allowed = true
	admissionReviewResponse.Response.Patch = patchBytes
	admissionReviewResponse.Response.PatchType = func() *v1.PatchType {
		pt := v1.PatchTypeJSONPatch
		return &pt
	}()

	// Return the AdmissionReview with a response as JSON.
	rbytes, err := json.Marshal(&admissionReviewResponse)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		err = fmt.Errorf("marshal admission review response error: %v", err)
		w.Write([]byte(err.Error()))
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(rbytes)
}
